#ifndef __CUHB_DUAL_BUF_H__
#define __CUHB_DUAL_BUF_H__

/*
  Copyright (c) 2010 Davide Rossetti (davide.rossetti@roma1.infn.it)

  This file is part of CuHB (CUDA Heisenberg) package.
  
  CuHB is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  CuHB is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with CuHB.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "hb_utils.h"

template <typename T> class dualbuf {
public:
    typedef T       elem_t;
    typedef elem_t* ptr_t;

    dualbuf(size_t nelem) : m_nelem(nelem), m_cpu_buf(0), m_gpu_buf(0) {
        buf_alloc();
    }
    ~dualbuf() {
        buf_free();
    }
    ptr_t cpu() { assert(m_cpu_buf); return m_cpu_buf; }
    ptr_t gpu() { assert(m_gpu_buf); return m_gpu_buf; }
    void cpu_to_gpu() {
        assert(m_gpu_buf);
        assert(m_cpu_buf);
        assert(m_nelem > 0);
        cuda_verify(cudaMemcpy(m_gpu_buf, m_cpu_buf, sizeof(elem_t)*m_nelem, cudaMemcpyHostToDevice));
    }
    void gpu_to_cpu() {
        assert(m_gpu_buf);
        assert(m_cpu_buf);
        assert(m_nelem > 0);
        cuda_verify(cudaMemcpy(m_cpu_buf, m_gpu_buf, sizeof(elem_t)*m_nelem, cudaMemcpyDeviceToHost));
    }

protected:
    void buf_alloc()
    {
        assert(!m_gpu_buf);
        assert(!m_cpu_buf);
        assert(m_nelem > 0);

        size_t buf_bytes = sizeof(elem_t)*m_nelem;

        cuda_verify(cudaMalloc((void**)&m_gpu_buf, buf_bytes));
        cuda_verify(cudaMemset(m_gpu_buf, 0, buf_bytes));

        m_cpu_buf = (ptr_t)::calloc(m_nelem, sizeof(elem_t));
        assert(m_cpu_buf);
    }
    void buf_free()
    {
        assert(m_gpu_buf);
        assert(m_cpu_buf);
        assert(m_nelem > 0);

        cuda_verify(cudaFree(m_gpu_buf));
        m_gpu_buf = 0;

        ::free(m_cpu_buf);
        m_cpu_buf = 0;

        m_nelem = 0;
    }

protected:
    size_t  m_nelem;
    ptr_t   m_cpu_buf;
    ptr_t   m_gpu_buf;
};

/*
 * Local variables:
 *  mode: c++
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 *  indent-tabs-mode: nil
 * End:
 */

#endif // __CUHB_DUAL_BUF_H__
